﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Query;
using SolutionExtender.AppCode;
using SolutionExtender.Interfaces;

namespace McTools.SolutionExtender.CustomEntitiesRecordsPlugin
{
    public partial class CustomEntitiesRecordControl : UserControl, IExportControl
    {
        #region Variables

        /// <summary>
        /// Microsoft Dynamics CRM 2011 Organization service
        /// </summary>
        private readonly IOrganizationService _service;

        /// <summary>
        /// Indicates if this control found rules
        /// </summary>
        private bool _hasData;

        #endregion Variables

        #region Constructor

        /// <summary>
        /// Initializes a new instance of class CustomEntitiesRecordControl
        /// </summary>
        /// <param name="service">Microsoft Dynamics CRM 2011 Organization service</param>
        public CustomEntitiesRecordControl(IOrganizationService service)
        {
            _service = service;
            InitializeComponent();
        }

        #endregion Constructor

        #region Methods

        /// <summary>
        /// Loads list of rules
        /// </summary>
        private void CustomEntitiesRecordControlLoad(object sender, EventArgs e)
        {
            pnlWaiting.Visible = true;

            var bWorker = new BackgroundWorker();
            bWorker.DoWork += BWorkerDoWork;
            bWorker.RunWorkerCompleted += BWorkerRunWorkerCompleted;
            bWorker.RunWorkerAsync();
        }

        void BWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            var items = new List<ListViewItem>();

            var request = new RetrieveAllEntitiesRequest { EntityFilters = EntityFilters.Entity };
            var response = (RetrieveAllEntitiesResponse)_service.Execute(request);

            foreach (var entity in response.EntityMetadata.Where(m => m.IsCustomEntity != null && m.IsCustomEntity.Value))
            {
                var label = entity.DisplayName.UserLocalizedLabel != null
                                   ? entity.DisplayName.UserLocalizedLabel.Label
                                   : entity.DisplayName.LocalizedLabels.Count > 0 ?
                                   entity.DisplayName.LocalizedLabels[0].Label :
                                   "N/A";

                var item = new ListViewItem(label);
                item.SubItems.Add(entity.LogicalName);
                item.Tag = entity;

                items.Add(item);
            }

            e.Result = items;
        }

        void BWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            pnlWaiting.Visible = false;

            if (e.Error != null)
            {

            }
            else
            {
                var items = (List<ListViewItem>) e.Result;

                if (items.Count() == 0)
                {
                    _hasData = false;
                    lvItems.Visible = false;
                    pnlNoItems.Visible = true;
                }
                else
                {
                    _hasData = true;
                    pnlNoItems.Visible = false;
                    lvItems.Items.AddRange(items.ToArray());
                }
            }
        }

        /// <summary>
        /// Gets a Xml list of selected rules
        /// </summary>
        /// <returns></returns>
        public XmlNodeList GetSelectedItems()
        {
            if (lvItems.SelectedItems.Count == 0 && _hasData)
            {
                pnlError.Visible = true;
                return null;
            }


            lblWaiting.Text = "Exporting selected records. Please wait...";
            lblWaiting.Invalidate();
            lblWaiting.Update();
            lblWaiting.Refresh();

            pnlWaiting.Visible = true;
            pnlWaiting.Invalidate();
            pnlWaiting.Update();
            pnlWaiting.Refresh();
            Application.DoEvents();

            pnlError.Visible = false;
            var stringifiedXml = new StringBuilder();
            stringifiedXml.Append("<Root>");

            foreach (ListViewItem item in lvItems.SelectedItems)
            {
                var entity = (EntityMetadata) item.Tag;

                var request = new RetrieveEntityRequest
                    {
                        EntityFilters = EntityFilters.Attributes,
                        LogicalName = entity.LogicalName
                    };

                entity = ((RetrieveEntityResponse) _service.Execute(request)).EntityMetadata;

                var qe = new QueryExpression(entity.LogicalName)
                    {
                        ColumnSet =
                            new ColumnSet(
                                entity.Attributes.Where(x => x.IsValidForCreate.Value).Select(attr => attr.LogicalName).ToArray())
                    };

                stringifiedXml.Append("<CustomEntitiesRecords>");
                stringifiedXml.Append("<" + entity.LogicalName + " primaryAttribute=\"" + entity.PrimaryNameAttribute + "\">");

                foreach(var record in _service.RetrieveMultiple(qe).Entities)
                {
                    stringifiedXml.Append(BuildReturnedString(record));
                }

                stringifiedXml.Append("</" + entity.LogicalName + ">");
                stringifiedXml.Append("</CustomEntitiesRecords>");
            }

            stringifiedXml.Append("</Root>");

            var doc = new XmlDocument();
            doc.LoadXml(stringifiedXml.ToString());

            return doc.DocumentElement.ChildNodes;
        }

        private string BuildReturnedString(Entity entity)
        {
            var mStream = new MemoryStream();
            var serializer = new DataContractSerializer(typeof(Entity),
                                                        null,
                                                        int.MaxValue,
                                                        false,
                                                        false,
                                                        null,
                                                        new KnownTypesResolver());

            var writer = new XmlTextWriter(mStream, Encoding.UTF8)
            {
                Formatting = Formatting.Indented
            };
            serializer.WriteObject(writer, entity);
            writer.Flush();
            mStream.Seek(0, SeekOrigin.Begin);
            var reader = new StreamReader(mStream);
            return reader.ReadToEnd();
        }

        #endregion Methods

        #region Handlers

        private void LvItemsColumnClick(object sender, ColumnClickEventArgs e)
        {
            lvItems.Sorting = lvItems.Sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;

            lvItems.ListViewItemSorter = new ListViewItemComparer(e.Column, lvItems.Sorting);
        }

        #endregion Handlers
    }
}
